package com.example.socket.socket.nio;

import com.example.socket.socket.bio.server.ChatHandler;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CharServer {


    private int DEFAULT_PORT=8888;

    private final String QUIT="quit";

    private static final int BUFFER=1024;
    //服务器端通道
    private ServerSocketChannel serverSocketChannel;
    //选择器
    private Selector selector;
    //用来读取消息
    private ByteBuffer rBuffer=ByteBuffer.allocate(BUFFER);
    //用来写入信息
    private ByteBuffer wBeBuffer=ByteBuffer.allocate(BUFFER);

    private Charset charset=Charset.forName("UTF-8");

    private int port;

    public CharServer(int port){
        this.port=port;
    }


    public boolean readyToQuit(String msg){
        return QUIT.equals(msg);
    }


    public void start(){
        try {
            //创建一个通道
            serverSocketChannel=ServerSocketChannel.open();
            //设置通道为未非阻塞
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            //创建selector
            selector=Selector.open();
            //监视服务器端连接时间
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("启动服务器，监听端口:"+port+".....");
            //进入监听模式
            while (true){
                selector.select();
                //获取监听事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey selectionKey : selectionKeys) {
                    //处理被触发事件
                    headles(selectionKey);
                }
                //把已处理的时间清空
                selectionKeys.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close(selector);
        }
    }

    private void headles(SelectionKey selectionKey) throws IOException {
        //ACCEPT时间--和客户建立了链接
        if(selectionKey.isAcceptable()){
            ServerSocketChannel server= (ServerSocketChannel)selectionKey.channel();
            SocketChannel client = server.accept();
            //设置非阻塞
            client.configureBlocking(false);
            //注册READ事件
            client.register(selector,SelectionKey.OP_READ);
            System.out.println("客户端["+client.socket().getPort()+"]"+"客户端链接了");
            //READ事件---客户发送消息
        }else if(selectionKey.isReadable()){
            SocketChannel client= (SocketChannel)selectionKey.channel();
            String fwMsg=receive(client);

            if(fwMsg.isEmpty()){
                //客户端异常
                selectionKey.cancel();
                //事件发生了变化，更新
                selector.wakeup();
            }else{
                //转发消息
                forwardMessage(client,fwMsg);
                //检查用户是否退出
                if(readyToQuit(fwMsg)){
                    selectionKey.cancel();
                    selector.wakeup();
                    System.out.println("客户端["+client.socket().getPort()+"]"+"断开链接了");
                }
            }
        }

    }
    //转发消息
    private void forwardMessage(SocketChannel client, String fwMsg) throws IOException {
        for (SelectionKey key:selector.keys()){
            Channel connectedClient=key.channel();
            if(key.channel() instanceof  ServerSocketChannel){
                continue;
            }
            //检测channel是否被关闭
            if(key.isValid()&&!client.equals(connectedClient)){
                wBeBuffer.clear();
                wBeBuffer.put(charset.encode(fwMsg));
                wBeBuffer.flip();
                while (wBeBuffer.hasRemaining()){
                    ((SocketChannel) connectedClient).write(wBeBuffer);
                }
            }
            System.out.println("客户端["+client.socket().getPort()+"]"+fwMsg);
        }

    }

    //读取channel上面的信息
    private String receive(SocketChannel client) throws IOException {
        rBuffer.clear();
        while (client.read(rBuffer)>0);
        rBuffer.flip();
        return String.valueOf(charset.decode(rBuffer));
    }

    public void close(Closeable closeable){
        if(closeable!=null){
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        CharServer charServer=new CharServer(8886);
        charServer.start();
    }
}
